﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Xml;
using System.IO;
using System.Web;

namespace FBNotifier
{
    public struct FacebookMessage
    {
        public FacebookMessage(string threadId, string updatedTime, string subject, string snippet, string snippetAuthorId, string snippetAuthorName)
        {
            this.threadId = threadId;
            this.updatedTime = updatedTime;
            this.subject = subject;
            this.snippet = snippet;
            this.snippetAuthorId = snippetAuthorId;
            this.snippetAuthorName = snippetAuthorName;
        }

        public string threadId;
        public string updatedTime;
        public string subject;
        public string snippet;
        public string snippetAuthorId;
        public string snippetAuthorName;
    };

    public struct FacebookNotification
    {
        public FacebookNotification(string notificationId, string titleText, string bodyText, string senderId, string createdTime, string url)
        {
            this.notificationId = notificationId;
            this.titleText = titleText;
            this.bodyText = bodyText;
            this.senderId = senderId;
            this.createdTime = createdTime;
            this.url = url;
        }

        public string notificationId;
        public string titleText;
        public string bodyText;
        public string senderId;
        public string createdTime;
        public string url;
    };

    static class FacebookAuxClass
    {
        private static List<FacebookNotification> currentNotifications = new List<FacebookNotification>();
        private static List<FacebookMessage> currentMessages = new List<FacebookMessage>();

        private static string GetQueryResult(string query)
        {
            string url = String.Format("https://api.facebook.com/method/fql.query?access_token={0}&query={1}", (Application.Current as App).AccessToken, Microsoft.JScript.GlobalObject.escape(query));

            string res = HTTPReader.ReadURL(url);

            return res;
        }

        private static string GetMultiQueryResult(params string[] queries)
        {
            StringBuilder jsonqueriessb = new StringBuilder("{");
            int idx = 0;

            foreach (string query in queries)
            {
                idx++;
                if (idx > 1)
                {
                    jsonqueriessb.Append(",");
                }

                jsonqueriessb.Append(String.Format("\"query{0}\":\"{1}\"", idx, query));
            }
            jsonqueriessb.Append("}");

            string url = String.Format("https://api.facebook.com/method/fql.multiquery?access_token={0}&queries={1}", (Application.Current as App).AccessToken, Microsoft.JScript.GlobalObject.escape(jsonqueriessb.ToString()));

            string res = HTTPReader.ReadURL(url);

            return res;
        }

        public static bool ReadNotifies(out List<FacebookNotification> allNotifications, out List<FacebookNotification> newNotifications)
        {
            string query = "SELECT notification_id, title_text, body_text, sender_id, created_time, href FROM notification WHERE recipient_id=me() AND is_unread = 1";

            string xmlResult = GetQueryResult(query);

            return ParseNotifications(xmlResult, out allNotifications, out newNotifications);
        }

        private static bool ParseNotifications(string xml, out List<FacebookNotification> allNotifications, out List<FacebookNotification> newNotifications)
        {
            bool bRes = false;

            allNotifications = new List<FacebookNotification>();
            newNotifications = new List<FacebookNotification>();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xml);

            // Create an XmlNamespaceManager to resolve the default namespace.
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
            nsmgr.AddNamespace("fb", "http://api.facebook.com/1.0/");

            var queryResponseNode = xmlDoc.SelectNodes("fb:fql_query_response", nsmgr);
            if (queryResponseNode.Count > 0)
            {
                var notificationNode = xmlDoc.SelectNodes("fb:fql_query_response/fb:notification", nsmgr);
                if (notificationNode.Count > 0)
                {
                    foreach (XmlNode node in notificationNode)
                    {
                        FacebookNotification notify;
                        notify.notificationId = node["notification_id"].InnerText;
                        notify.titleText = node["title_text"].InnerText;
                        notify.bodyText = node["body_text"].InnerText;
                        notify.senderId = node["sender_id"].InnerText;
                        notify.createdTime = node["created_time"].InnerText;
                        notify.url = node["href"].InnerText;

                        allNotifications.Add(notify);
                    }

                    foreach (FacebookNotification notify in allNotifications)
                    {
                        if (!currentNotifications.Exists(n => n.notificationId == notify.notificationId))
                        {
                            newNotifications.Add(notify);
                        }
                    }

                    currentNotifications = allNotifications;
                }

                bRes = true;
            }

            return bRes;
        }

        public static void GetHTMLForNotifies(List<FacebookNotification> notifications, out string html)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("<table width=\"100%\">");

            int rowNumber = 0;

            foreach (FacebookNotification notify in notifications)
            {
                rowNumber++;

                string subject = notify.titleText;
                string snippet = notify.bodyText;
                string snippet_author = notify.senderId;
                string time = GetTimeSpanString(ConvertFBTimestamp(double.Parse(notify.createdTime)));

                if (rowNumber % 2 == 0)
                {
                    sb.AppendLine(String.Format("<tr onClick=\"window.external.LinkClicked('{0}');\" style=\"background-color: #EFF1F7\" style=\"cursor:pointer\"><td>", notify.url));
                }
                else
                {
                    sb.AppendLine(String.Format("<tr onClick=\"window.external.LinkClicked('{0}');\" style=\"cursor:pointer\"><td>", notify.url));
                }

                string photourl = String.Format("http://graph.facebook.com/{0}/picture", snippet_author);

                string subjectFont = "style=\"font-family: Segoe UI; color: #444; font-size: 12; font-weight: bold\"";
                string snippetFont = "style=\"font-family: Segoe UI; color: #444; font-size: 12;\"";
                string timeFont = "style=\"font-family: Segoe UI; color: #444; font-size: 12; font-style: italic; color:Gray\"";

                string table = String.Format("<table><tr  valign=\"top\"><td><img src=\"{0}\"></td><td><span {1}>{2}</span><br/><span {3}>{4}</span><br/><span {5}>{6}</span></td></tr></table>", photourl, subjectFont, subject, snippetFont, snippet, timeFont, time);

                sb.AppendLine(table);

                sb.AppendLine("</td></tr>");
            }

            sb.AppendLine("</table>");

            html = sb.ToString();
        }

        public static bool ReadMessages(out List<FacebookMessage> allMessages, out List<FacebookMessage> newMessages)
        {
            string query1 = "SELECT thread_id, updated_time, subject, snippet, snippet_author FROM thread WHERE unread != 0 AND folder_id = 0";
            string query2 = "SELECT uid, name FROM user WHERE uid IN (SELECT snippet_author FROM #query1)";

            string xmlResult = GetMultiQueryResult(query1, query2);

            return ParseMessages(xmlResult, out allMessages, out newMessages);
        }

        private static bool ParseMessages(string xmlResult, out List<FacebookMessage> allMessages, out List<FacebookMessage> newMessages)
        {
            bool bRes = false;

            allMessages = new List<FacebookMessage>();
            newMessages = new List<FacebookMessage>();
            Dictionary<string, string> usersDict = new Dictionary<string, string>();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlResult);

            // Create an XmlNamespaceManager to resolve the default namespace.
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
            nsmgr.AddNamespace("fb", "http://api.facebook.com/1.0/");

            var queryResponseNode = xmlDoc.SelectNodes("fb:fql_multiquery_response", nsmgr);
            if (queryResponseNode.Count > 0)
            {
                var usersNode = xmlDoc.SelectNodes("fb:fql_multiquery_response/fb:fql_result[2]/fb:fql_result_set/fb:user", nsmgr);
                var messagesNode = xmlDoc.SelectNodes("fb:fql_multiquery_response/fb:fql_result[1]/fb:fql_result_set/fb:thread", nsmgr);

                if (usersNode.Count > 0 && messagesNode.Count > 0)
                {
                    foreach (XmlNode node in usersNode)
                    {
                        string name = node["name"].InnerText;
                        string uid = node["uid"].InnerText;

                        usersDict[uid] = name;
                    }

                    foreach (XmlNode node in messagesNode)
                    {
                        FacebookMessage message;

                        message.threadId = node["thread_id"].InnerText;
                        message.updatedTime = node["updated_time"].InnerText;
                        message.subject = node["subject"].InnerText;
                        message.snippet = node["snippet"].InnerText;
                        message.snippetAuthorId = node["snippet_author"].InnerText;
                        if (usersDict.TryGetValue(message.snippetAuthorId, out message.snippetAuthorName) == false)
                        {
                            message.snippetAuthorName = "Unknown";
                        }

                        allMessages.Add(message);
                    }

                    foreach (FacebookMessage message in allMessages)
                    {
                        if (!currentMessages.Exists(m => m.threadId == message.threadId && m.updatedTime == message.updatedTime))
                        {
                            newMessages.Add(message);
                        }
                    }

                    currentMessages = allMessages;
                }

                bRes = true;
            }

            return bRes;
        }

        public static void GetHTMLForMessages(List<FacebookMessage> messages, out string html)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("<table width=\"100%\">");

            int rowNumber = 0;

            foreach (FacebookMessage message in messages)
            {
                rowNumber++;

                string subject = message.subject;
                string snippet = message.snippet;
                string snippet_author = message.snippetAuthorId;
                string snippet_author_name = message.snippetAuthorName;
                string time = GetTimeSpanString(ConvertFBTimestamp(double.Parse(message.updatedTime)));

                string url = String.Format("http://www.facebook.com/?sk=messages&tid={0}", message.threadId);
                if (rowNumber % 2 == 0)
                {
                    sb.AppendLine(String.Format("<tr style=\"background-color: #EFF1F7\" onClick=\"window.external.LinkClicked('{0}');\" style=\"cursor:pointer\"><td>", url));
                }
                else
                {
                    sb.AppendLine(String.Format("<tr onClick=\"window.external.LinkClicked('{0}');\" style=\"cursor:pointer\"><td>", url));
                }

                string photourl = String.Format("http://graph.facebook.com/{0}/picture", snippet_author);

                string subjectFont = "style=\"font-family: Segoe UI; color: #444; font-size: 12; font-weight: bold\"";
                string snippetFont = "style=\"font-family: Segoe UI; color: #444; font-size: 12;\"";
                string authorFont = "style=\"font-family: Segoe UI; color: Blue; font-size: 12; font-style: italic\"";
                string timeFont = "style=\"font-family: Segoe UI; color: #444; font-size: 12; font-style: italic; color:Gray\"";

                string table = String.Format("<table><tr valign=\"top\"><td><img src=\"{0}\"></td><td><span {1}>{2} : </span><span {3}>{4}</span><br/><span {5}>{6}</span><br/><span {7}>{8}</span></td></tr></table>", photourl, authorFont, snippet_author_name, subjectFont, subject, snippetFont, snippet, timeFont, time);

                sb.AppendLine(table);

                sb.AppendLine("</td></tr>");
            }

            sb.AppendLine("</table>");

            html = sb.ToString();
        }

        public static void ReadProfileName(out string profileName)
        {
            string query1 = "SELECT name FROM user WHERE uid = me();";

            string xmlResult = GetQueryResult(query1);

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlResult);

            // Create an XmlNamespaceManager to resolve the default namespace.
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
            nsmgr.AddNamespace("fb", "http://api.facebook.com/1.0/");

            XmlNode node = xmlDoc.SelectSingleNode("fb:fql_query_response/fb:user/fb:name", nsmgr);

            if (node != null)
            {
                profileName = node.InnerText;
            }
            else
            {
                profileName = "connection error";
            }
        }

        public static string GetUnreadMessagesCount()
        {
            string query = "SELECT unread_count FROM mailbox_folder WHERE folder_id=0";

            return GetQueryResult(query);
        }

        public static DateTime ConvertFBTimestamp(double timestamp)
        {
            //create a new DateTime value based on the Unix Epoch
            DateTime converted = new DateTime(1970, 1, 1, 0, 0, 0, 0);

            //add the timestamp to the value
            DateTime newDateTime = converted.AddSeconds(timestamp);

            //return the value in string format
            return newDateTime.ToLocalTime();
        }

        public static string GetTimeSpanString(DateTime time)
        {
            string res = "";

            TimeSpan ts = DateTime.Now - time;

            if (ts.Days == 0)
            {
                if (ts.Hours == 0)
                {
                    if (ts.Minutes == 0)
                    {
                        res = ts.Seconds + " seconds ago";
                    }
                    else
                    {
                        if (ts.Minutes == 1)
                        {
                            res = ts.Minutes + " minute ago";
                        }
                        else
                        {
                            res = ts.Minutes + " minutes ago";
                        }
                    }
                }
                else
                {
                    if (ts.Hours == 1)
                    {
                        res = ts.Hours + " hour ago";
                    }
                    else
                    {
                        res = ts.Hours + " hours ago";
                    } 
                }
            }
            else
            {
                if (ts.Days == 1)
                {
                    res = ts.Days + " day ago";
                }
                else
                {
                    res = ts.Days + " days ago";
                }
            }

            return res;
        }
    }
}
